<template>
  <!-- 画布充满屏幕，图片按照原始大小渲染 -->
  <div
    :style="{
      transform: frameStyle.transform,
      width: frameStyle.width + 'px',
      height: frameStyle.height + 'px',
    }"
  >
    <canvas
      :width="frameStyle.width"
      :height="frameStyle.height"
      ref="canvas"
      @mousedown="startDrawing"
      @mouseup="stopDrawing"
    ></canvas>
  </div>
</template>
<script lang="ts" setup>
import { onMounted } from 'vue'
import { reactive } from 'vue'
import { ref, computed } from 'vue'
const emit = defineEmits(['done'])
const props = defineProps({
  frameRect: {
    type: Object,
    default: () => ({}),
  },
  imgDom: {
    type: Object,
    default: () => ({}),
  },
})

const lineWidth = 1

const frameStyle = computed(() => {
  const { angle, zoom, width, height, x, y } = props.frameRect
  const { rotatedWidth, rotatedHeight } = calculateRotatedRectangleSize(
    width,
    height,
    angle,
  )
  return {
    width: rotatedWidth * zoom,
    height: rotatedHeight * zoom,
    left: x,
    top: y,
    transform: `translate(${x * zoom}px, ${y * zoom}px)`,
  }
})

const rect = reactive({
  x: 0,
  y: 0,
  width: 0,
  height: 0,
})

const canvas = ref()
onMounted(() => {
  renderImage()
})

/**
 * 截图
 */
function screenShot() {
  const ctx = canvas.value.getContext('2d')
  // 只渲染图片，然后再截图对应区域，避免截到边框
  ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
  renderImage()
  const imgData = ctx.getImageData(rect.x, rect.y, rect.width, rect.height)
  const canvas2 = document.createElement('canvas')
  canvas2.width = rect.width
  canvas2.height = rect.height
  const ctx2 = canvas2.getContext('2d') as CanvasRenderingContext2D
  ctx2.putImageData(imgData, 0, 0)
  const dataUrl = canvas2.toDataURL('image/png')
  const { width, height, zoom, angle } = props.frameRect
  // 将角度转换为弧度
  const rotationAngleInRadians = angle * (Math.PI / 180)
  // x轴方向上偏移量
  const offsetX = height * zoom * Math.sin(rotationAngleInRadians)
  // y轴方向上偏移量
  const offsetY = width * zoom * Math.sin(rotationAngleInRadians)
  emit('done', {
    dataUrl,
    // 以图片左上角为原点，计算截图区域在图片中的位置
    rect: {
      x: rect.x - offsetX,
      y: rect.y - offsetY,
      width: rect.width,
      height: rect.height,
    },
  })
}

/**
 * 画矩形
 */
function startDrawing(e: MouseEvent) {
  const { x, y } = getMousePos(e)
  rect.x = x
  rect.y = y
  const ctx = canvas.value.getContext('2d')
  ctx.strokeStyle = '#00f'
  ctx.lineWidth = lineWidth
  ctx.beginPath()
  ctx.moveTo(x, y)
  canvas.value.addEventListener('mousemove', draw)
}

function draw(e: MouseEvent) {
  const { x, y } = getMousePos(e)
  const ctx = canvas.value.getContext('2d')
  ctx.clearRect(0, 0, canvas.value.width, canvas.value.height)
  renderImage()
  rect.width = x - rect.x
  rect.height = y - rect.y
  ctx.strokeRect(rect.x, rect.y, rect.width, rect.height)
}

function stopDrawing(e: MouseEvent) {
  const { x, y } = getMousePos(e)
  rect.width = x - rect.x
  rect.height = y - rect.y
  canvas.value.removeEventListener('mousemove', draw)
  screenShot()
}

function getMousePos(e: MouseEvent) {
  const rect = canvas.value.getBoundingClientRect()
  return {
    x: e.clientX - rect.left,
    y: e.clientY - rect.top,
  }
}

/**
 * 渲染图片
 */
function renderImage() {
  const ctx = canvas.value.getContext('2d')
  // 保存当前的绘图状态
  ctx.save()
  const { width, height, zoom } = props.frameRect
  // 将绘图原点移动到图像中心
  ctx.translate(canvas.value.width / 2, canvas.value.height / 2)
  // 旋转角度
  ctx.rotate((props.frameRect.angle * Math.PI) / 180)
  // 绘制图片
  ctx.drawImage(
    props.imgDom,
    (-width * zoom) / 2,
    (-height * zoom) / 2,
    width * zoom,
    height * zoom,
  )
  // 恢复绘图状态
  ctx.restore()
}

/**
 * 计算旋转后的宽度
 * @param width 原始宽度
 * @param height 原始高度
 * @param rotationAngleInDegrees 旋转角度
 */
function calculateRotatedRectangleSize(
  width: number,
  height: number,
  rotationAngleInDegrees: number,
) {
  // 将角度转换为弧度
  const rotationAngleInRadians = rotationAngleInDegrees * (Math.PI / 180)

  // 计算旋转后的宽度和高度
  const rotatedWidth =
    Math.abs(width * Math.cos(rotationAngleInRadians)) +
    Math.abs(height * Math.sin(rotationAngleInRadians))
  const rotatedHeight =
    Math.abs(width * Math.sin(rotationAngleInRadians)) +
    Math.abs(height * Math.cos(rotationAngleInRadians))

  return { rotatedWidth, rotatedHeight }
}
</script>
